package baiqitun.stupro.project.service.impl;

import baiqitun.stupro.common.exception.StuproException;
import baiqitun.stupro.common.exception.enums.support.ExistedExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.StuproExceptionEnum;
import baiqitun.stupro.common.exception.support.ExistedException;
import baiqitun.stupro.common.exception.support.RelationExistedException;
import baiqitun.stupro.common.service.support.MyServiceImpl;
import baiqitun.stupro.common.workflow.enums.ProcessDefinitionKeyEnum;
import baiqitun.stupro.common.workflow.enums.ProcessVariableEnum;
import baiqitun.stupro.common.workflow.util.RuntimeTaskUtil;
import baiqitun.stupro.project.constant.ProjectProcessVariableEnum;
import baiqitun.stupro.project.constant.SysProjectRoleEnum;
import baiqitun.stupro.project.constant.SysProjectTypeEnum;
import baiqitun.stupro.project.core.document.entity.SysProjectDocument;
import baiqitun.stupro.project.core.news.SysProjectNews;
import baiqitun.stupro.project.core.news.constant.SysProjectNewsObjectTypeEnum;
import baiqitun.stupro.project.core.news.service.SysProjectNewsService;
import baiqitun.stupro.project.core.process.InviteUserToProjectProcess;
import baiqitun.stupro.project.entity.*;
import baiqitun.stupro.project.mapper.SysProjectUserRoleMapper;
import baiqitun.stupro.project.service.*;
import baiqitun.stupro.security.enums.SysRoleEnum;
import baiqitun.stupro.security.entity.SysRole;
import baiqitun.stupro.security.entity.SysUser;
import baiqitun.stupro.security.service.SysUserRoleService;
import baiqitun.stupro.security.service.SysUserService;
import baiqitun.stupro.subject.entity.SysStudentInfo;
import baiqitun.stupro.subject.service.SysStudentInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 项目成员角色表 服务实现类
 * </p>
 *
 * @author kedaji
 * @since 2020-04-18
 */
@Service
public class SysProjectUserRoleServiceImpl extends MyServiceImpl<SysProjectUserRoleMapper, SysProjectUserRole> implements SysProjectUserRoleService {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysProjectService sysProjectService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysProjectUserService sysProjectUserService;
    @Autowired
    private SysProjectRoleService sysProjectRoleService;
    @Autowired
    private SysProjectNewsService sysProjectNewsService;
    @Autowired
    private SysStudentInfoService sysStudentInfoService;
    @Autowired
    private RuntimeTaskUtil runtimeTaskUtil;
    @Autowired
    private InviteUserToProjectProcess inviteUserToProjectProcess;


    /**
     * 创建一个项目
     * @param project 项目信息
     * @param document 项目说明文档
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveFreeProject(SysProject project, SysProjectDocument document) {
        try{
            SysUser owner = sysUserService.getUserByUsername(project.getCreateBy());
            sysUserService.checkEnabled(owner.getId());
            project.setType(SysProjectTypeEnum.FREE.getValue());
            sysProjectService.saveWithDocument(project, document);
            saveUserToProject(project.getId(), owner.getId(), project.getCreateBy());
            grantRoleToUser(project.getId(), owner.getId(), SysProjectRoleEnum.OWNER.name(), owner.getCreateBy());

            // 创建一条项目动态
            SysProjectNews projectNews = new SysProjectNews();
            projectNews.setProjectId(project.getId()).setUserId(owner.getId()).setObjectType(SysProjectNewsObjectTypeEnum.PROJECT.name()).setCreateBy(owner.getUsername());
            sysProjectNewsService.saveCreateTypeNews(projectNews);
        } catch (Exception e){
            sysProjectService.removeDocumentById(document.getId());
            throw e;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveSubjectProject(SysProject project, SysProjectDocument document) {
        // 想要创建一生一品项目的学生必须选择一生一品导师
        String ownerUserId = sysUserService.getUserByUsername(project.getCreateBy()).getId();
        List<SysRole> roles = sysUserRoleService.listRoleByUserId(ownerUserId);
        String roleCode = "";

        // 先判断当前用户是学生还是导师
        for (SysRole role: roles){
            if (SysRoleEnum.ROLE_TUTOR.name().equals(role.getCode())){
                roleCode = SysRoleEnum.ROLE_TUTOR.name();
                break;
            } else if (SysRoleEnum.ROLE_STUDENT.name().equals(role.getCode())){
                // 判断学生是否选择了一生一品导师
                SysStudentInfo studentInfo = sysStudentInfoService.getStudentInfoByUserId(ownerUserId);
                if (StringUtils.isBlank(studentInfo.getTutorId())){
                    throw new StuproException(StuproExceptionEnum.ILLEGAL_OPERATION);
                }
                roleCode = SysRoleEnum.ROLE_STUDENT.name();
                break;
            }
        }

        if (StringUtils.isBlank(roleCode)){
            // 如果这里抛出异常说明创建项目的用户既不是学生也不是导师
            throw new StuproException(StuproExceptionEnum.ILLEGAL_OPERATION);
        }

        // 创建一生一品系统需要在通过管理员审核后才能创建
        String rootUserId = sysUserService.getUserByUsername("root").getId();
        HashMap<String, Object> variables = new HashMap<>(3);
        // 项目名称
        variables.put(ProcessVariableEnum.NAME.name(), project.getName());
        // 项目说明文档内容
        variables.put(ProcessVariableEnum.DESCRIPTION.name(), document.getContent());
        // 项目审核人
        variables.put(ProcessVariableEnum.ASSIGNEE.name(), rootUserId);
        // 项目说明文档类型
        variables.put(ProjectProcessVariableEnum.DOCUMENT_TYPE.name(), document.getType());
        // 项目公开范围
        variables.put(ProjectProcessVariableEnum.SCOPE.name(), project.getScope());
        // 向管理员发送申请
        runtimeTaskUtil.startProcessInstance(ProcessDefinitionKeyEnum.CREATE_SUBJECT_PROJECT.name(), null, ownerUserId, variables);
    }


    /**
     * 所有项目成员必定有DEVELOPER的角色
     * @param projectId 项目id
     * @param userId 用户id
     * @param createBy 创建者用户名
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveUserToProject(String projectId, String userId,String createBy) {
        sysProjectUserService.checkRelationExisted(projectId, userId);
        SysProjectUser relation = new SysProjectUser();
        relation.setProjectId(projectId).setUserId(userId).setCreateBy(createBy);
        sysProjectUserService.save(relation);
        grantRoleToUser(projectId, userId, SysProjectRoleEnum.DEVELOPER.name(), createBy);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void inviteTutors(String projectId, String ownerUserId, List<String> tutorUserIds, String description) {
        SysUser owner = sysUserService.getEnabledUser(ownerUserId);
        for (String tutorUserId: tutorUserIds){
            sysUserService.checkEnabled(tutorUserId);
            sysUserRoleService.checkUserHasRole(tutorUserId, SysRoleEnum.ROLE_TUTOR);
        }
        for (String tutorUserId: tutorUserIds){
            inviteUserToProjectProcess.sendInvitation(projectId, ownerUserId, tutorUserId, description);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void inviteStudents(String projectId, String ownerUserId, List<String> studentUserIds, String description) {
        SysUser owner = sysUserService.getEnabledUser(ownerUserId);
        //检查选择的学生是否是项目导师带领的学生
        List<SysUser> tutors = sysProjectUserService.listTutorByProjectId(projectId);

        for (String studentUserId: studentUserIds){
            sysUserService.checkEnabled(studentUserId);
            sysUserRoleService.checkUserHasRole(studentUserId, SysRoleEnum.ROLE_STUDENT);
            boolean checked = false;
            for (SysUser tutor: tutors){
                SysStudentInfo studentInfo = sysStudentInfoService.getStudentInfoByUserId(studentUserId);
                if (studentInfo.getTutorId().equals(tutor.getId())){
                    checked = true;
                    break;
                }
            }
            if (! checked){
                throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER);
            }
        }

        for (String studentUserId: studentUserIds){
            inviteUserToProjectProcess.sendInvitation(projectId, ownerUserId, studentUserId, description);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void transfer(String projectId, String ownerUserId, String targetUserId) {

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(String projectId, String updateBy) {
        sysProjectService.checkExisted(projectId, new ExistedException(ExistedExceptionEnum.PROJECT_EXISTED));
        // 删除用户与项目的关系
        SysProjectUser relation = new SysProjectUser();
        relation.setProjectId(projectId);
        sysProjectUserService.remove(new QueryWrapper<>(relation));
        // 删除项目本身
        SysProject project = new SysProject();
        project.setId(projectId).setUpdateBy(updateBy);
        sysProjectService.updateById(project);
        return sysProjectService.removeById(projectId);
    }

    /**
     * 向项目成员授予角色权限
     * @param projectId 项目id
     * @param userId 角色id
     * @param projectRoleCode 项目角色代码
     * @param createBy 创建人用户名
     */
    @Override
    public void grantRoleToUser(String projectId, String userId, String projectRoleCode, String createBy){
        sysUserService.checkEnabled(userId);
        sysProjectService.checkExisted(projectId, new ExistedException(ExistedExceptionEnum.PROJECT_EXISTED));
        // 只有用户已经被添加到项目中才能给这个用户授予权限
        sysProjectUserService.getExistedRelation(projectId, userId);
        SysProjectUser projectUserRelation = sysProjectUserService.getExistedRelation(projectId, userId);
        SysProjectRole projectRole = sysProjectRoleService.getProjectRoleByCode(projectRoleCode);
        checkRelationExisted(projectUserRelation.getId(), projectRole.getId());

        SysProjectUserRole roleRelation = new SysProjectUserRole();
        roleRelation.setProjectUserId(projectUserRelation.getId()).setRoleId(projectRole.getId()).setCreateBy(createBy);
        super.save(roleRelation);
    }

    private void checkRelationExisted(String projectUserId, String roleId){
        SysProjectUserRole queryProjectUserRole = new SysProjectUserRole();
        queryProjectUserRole.setProjectUserId(projectUserId).setRoleId(roleId);
        SysProjectUserRole relation = getOne(new QueryWrapper<>(queryProjectUserRole));
        if (relation != null){
            throw new RelationExistedException(ExistedExceptionEnum.PROJECT_USER_RELATION_EXISTED)
                    .put(SysProjectUser.class,"id", projectUserId)
                    .put(SysProjectRole.class, "id", roleId);
        }
    }
}
